package mysql

import (
	"fmt"
	"gitee.com/dreamwood/ez"
	"strconv"
)

const (
	F_TEXT     = "string"
	F_INT      = "int"
	F_BOOL     = "bool"
	F_FLOAT    = "float64"
	F_DATETIME = "time.Time"
	F_ARRAY    = "array"
	F_M2O      = "*"
	F_O2M      = "[]*"
	F_O2O      = "*"
	F_M2M      = "[]*"
	F_ART      = "string"
	F_JSON     = "string"
	F_FILE     = "orm.File"
	F_FILES    = "orm.Files"
)

type Field struct {
	CnName        string `json:"cnName" groups:"app" orm:"null;description(字段名中文);on_delete(set_null);size(20);"`
	Name          string `json:"name" groups:"app" orm:"null;description(字段名);on_delete(set_null);size(20);"`
	Type          int    `json:"type" groups:"app" orm:"null;description(数据类型);on_delete(set_null);rel(fk);"`
	Length        string `json:"length" groups:"app" orm:"null;description(长度);on_delete(set_null);size(255);"`
	Nullable      bool   `json:"nullable" groups:"app" orm:"null;description(语序为空);on_delete(set_null);"`
	DataSrc       string `json:"dataSrc" groups:"app" orm:"null;description(数据源);on_delete(set_null);size(64);"`
	DataSrControl bool   `json:"dataSrControl" groups:"app" orm:"null;description(主控);on_delete(set_null);"`
	DefaultValue  string `json:"defaultValue" groups:"app" orm:"null;description(默认值);on_delete(set_null);size(255);"`
	OrmParam      string `json:"ormParam" groups:"app" orm:"null;description(orm参数);on_delete(set_null);size(255);"`
}
type BaseType struct {
	Name         string
	Type         string
	EntityType   string
	CnName       string
	Nullable     bool
	DefaultValue string
	Orm          string
	Src          string
	SrcDir       string
	Desc         string
}

func (this *BaseType) SetName(v string) *BaseType         { this.Name = v; return this }
func (this *BaseType) SetType(v string) *BaseType         { this.Type = v; return this }
func (this *BaseType) SetEntityType(v string) *BaseType   { this.EntityType = v; return this }
func (this *BaseType) SetCnName(v string) *BaseType       { this.CnName = v; return this }
func (this *BaseType) SetNullable(v bool) *BaseType       { this.Nullable = v; return this }
func (this *BaseType) SetDefaultValue(v string) *BaseType { this.DefaultValue = v; return this }
func (this *BaseType) SetOrm(v string) *BaseType          { this.Orm = v; return this }
func (this *BaseType) SetSrc(v string) *BaseType          { this.Src = v; return this }
func (this *BaseType) SetDesc(v string) *BaseType         { this.Desc = v; return this }

func (this BaseType) GetConfig() BaseType {
	return this
}
func (this BaseType) GetOrm() string {
	return this.Orm
}
func (this BaseType) GetName() string {
	return this.Name
}
func (this BaseType) GetType() string {
	return this.Type
}
func (this BaseType) GetEntityType() string {
	return this.EntityType
}
func (this BaseType) GetSrc() string {
	return this.Src
}
func (this BaseType) GetSrcDir() string {
	return this.SrcDir
}

type FieldItem interface {
	GetConfig() BaseType
	GetName() string
	GetType() string
	GetEntityType() string
	GetSrc() string
	GetSrcDir() string
	GetOrm() string
	CreateConfig()
}

type Text struct {
	BaseType
	Length string
}

func (this *Text) SetName(v string) *Text         { this.Name = v; return this }
func (this *Text) SetType(v string) *Text         { this.Type = v; return this }
func (this *Text) SetEntityType(v string) *Text   { this.EntityType = v; return this }
func (this *Text) SetCnName(v string) *Text       { this.CnName = v; return this }
func (this *Text) SetNullable(v bool) *Text       { this.Nullable = v; return this }
func (this *Text) SetDefaultValue(v string) *Text { this.DefaultValue = v; return this }
func (this *Text) SetOrm(v string) *Text          { this.Orm = v; return this }
func (this *Text) SetSrc(v string) *Text          { this.Src = v; return this }

func (this *Text) SetLength(v int) *Text { this.Length = strconv.Itoa(v); return this }

func (text *Text) CreateConfig() {
	text.Type = F_TEXT
	isNull := ""
	if !text.Nullable {
		isNull = "not null;"
	}
	if text.Length == "" {
		text.Length = "255;"
	}
	text.Orm = fmt.Sprintf(`gorm:"%ssize:%s;"`, isNull, text.Length)
}

type Art struct {
	BaseType
}

func (this *Art) SetName(v string) *Art         { this.Name = v; return this }
func (this *Art) SetType(v string) *Art         { this.Type = v; return this }
func (this *Art) SetEntityType(v string) *Art   { this.EntityType = v; return this }
func (this *Art) SetCnName(v string) *Art       { this.CnName = v; return this }
func (this *Art) SetNullable(v bool) *Art       { this.Nullable = v; return this }
func (this *Art) SetDefaultValue(v string) *Art { this.DefaultValue = v; return this }
func (this *Art) SetOrm(v string) *Art          { this.Orm = v; return this }
func (this *Art) SetSrc(v string) *Art          { this.Src = v; return this }

func (art *Art) CreateConfig() {
	art.Type = F_ART
	isNull := ""
	if !art.Nullable {
		isNull = "not null;"
	}
	art.Orm = fmt.Sprintf(`gorm:"%stype:text;"`, isNull)
}

type Json struct {
	BaseType
}

func (this *Json) SetName(v string) *Json         { this.Name = v; return this }
func (this *Json) SetType(v string) *Json         { this.Type = v; return this }
func (this *Json) SetEntityType(v string) *Json   { this.EntityType = v; return this }
func (this *Json) SetCnName(v string) *Json       { this.CnName = v; return this }
func (this *Json) SetNullable(v bool) *Json       { this.Nullable = v; return this }
func (this *Json) SetDefaultValue(v string) *Json { this.DefaultValue = v; return this }
func (this *Json) SetOrm(v string) *Json          { this.Orm = v; return this }
func (this *Json) SetSrc(v string) *Json          { this.Src = v; return this }

func (json *Json) CreateConfig() {
	json.Type = F_JSON
	json.EntityType = "JSON"
	isNull := ""
	if !json.Nullable {
		isNull = "not null;"
	}
	json.Orm = fmt.Sprintf(`gorm:"%stype(text);" edit_type:"json"`, isNull)
}

type Bool struct {
	BaseType
}

func (this *Bool) SetName(v string) *Bool         { this.Name = v; return this }
func (this *Bool) SetType(v string) *Bool         { this.Type = v; return this }
func (this *Bool) SetEntityType(v string) *Bool   { this.EntityType = v; return this }
func (this *Bool) SetCnName(v string) *Bool       { this.CnName = v; return this }
func (this *Bool) SetNullable(v bool) *Bool       { this.Nullable = v; return this }
func (this *Bool) SetDefaultValue(v string) *Bool { this.DefaultValue = v; return this }
func (this *Bool) SetOrm(v string) *Bool          { this.Orm = v; return this }
func (this *Bool) SetSrc(v string) *Bool          { this.Src = v; return this }

func (f *Bool) CreateConfig() {
	f.Type = F_BOOL
	isNull := ""
	if !f.Nullable {
		isNull = "not null;"
	}
	f.Orm = fmt.Sprintf(`gorm:"%s"`, isNull)
}

type Int struct {
	BaseType
	Base string
}

func (this *Int) SetName(v string) *Int         { this.Name = v; return this }
func (this *Int) SetType(v string) *Int         { this.Type = v; return this }
func (this *Int) SetEntityType(v string) *Int   { this.EntityType = v; return this }
func (this *Int) SetCnName(v string) *Int       { this.CnName = v; return this }
func (this *Int) SetNullable(v bool) *Int       { this.Nullable = v; return this }
func (this *Int) SetDefaultValue(v string) *Int { this.DefaultValue = v; return this }
func (this *Int) SetOrm(v string) *Int          { this.Orm = v; return this }
func (this *Int) SetSrc(v string) *Int          { this.Src = v; return this }
func (this *Int) SetBase(v string) *Int         { this.Base = v; return this }

func (f *Int) CreateConfig() {
	f.Type = F_INT + f.Base
	isNull := ""
	if !f.Nullable {
		isNull = "not null;"
	}
	f.Orm = fmt.Sprintf(`gorm:"%s"`, isNull)
}

type Float struct {
	BaseType
	Base     string
	Digits   int    //数字位长度
	Decimals int    //小数位精度
	OrmParam string //digits(12); decimals(4);
}

func (this *Float) SetName(v string) *Float         { this.Name = v; return this }
func (this *Float) SetType(v string) *Float         { this.Type = v; return this }
func (this *Float) SetEntityType(v string) *Float   { this.EntityType = v; return this }
func (this *Float) SetCnName(v string) *Float       { this.CnName = v; return this }
func (this *Float) SetNullable(v bool) *Float       { this.Nullable = v; return this }
func (this *Float) SetDefaultValue(v string) *Float { this.DefaultValue = v; return this }
func (this *Float) SetOrm(v string) *Float          { this.Orm = v; return this }
func (this *Float) SetSrc(v string) *Float          { this.Src = v; return this }
func (this *Float) SetBase(v string) *Float         { this.Base = v; return this }

// 设置整数位长度
func (f *Float) SetDigits(dig int) *Float {
	f.Digits = dig
	if f.Decimals == 0 {
		f.Decimals = 4
	}
	f.OrmParam = fmt.Sprintf("digits(%d); decimals(%d);", f.Digits, f.Decimals)
	return f
}

// 设置小数位保留精度
func (f *Float) SetDecimals(dec int) *Float {
	f.Decimals = dec
	if f.Digits == 0 {
		f.Digits = 12
	}
	f.OrmParam = fmt.Sprintf("digits(%d); decimals(%d);", f.Digits, f.Decimals)
	return f
}
func (f *Float) CreateConfig() {
	f.Type = F_FLOAT + f.Base
	isNull := ""
	if !f.Nullable {
		isNull = "not null;"
	}
	f.Orm = fmt.Sprintf(`gorm:"%s%s"`, isNull, f.OrmParam)
}

type DateTime struct {
	BaseType
	IsAutoNow    bool
	IsAutoNowAdd bool
	OrmParam     string //auto_now; auto_now_add;
}

func (this *DateTime) SetName(v string) *DateTime         { this.Name = v; return this }
func (this *DateTime) SetType(v string) *DateTime         { this.Type = v; return this }
func (this *DateTime) SetEntityType(v string) *DateTime   { this.EntityType = v; return this }
func (this *DateTime) SetCnName(v string) *DateTime       { this.CnName = v; return this }
func (this *DateTime) SetNullable(v bool) *DateTime       { this.Nullable = v; return this }
func (this *DateTime) SetDefaultValue(v string) *DateTime { this.DefaultValue = v; return this }
func (this *DateTime) SetOrm(v string) *DateTime          { this.Orm = v; return this }
func (this *DateTime) SetSrc(v string) *DateTime          { this.Src = v; return this }

// 是否在更新时自动填充
func (this *DateTime) SetIsAutoNow(is bool) *DateTime {
	ormP := ""
	this.IsAutoNow = is
	if this.IsAutoNowAdd {
		ormP += "autoCreateTime;"
	}
	if this.IsAutoNow {
		ormP += "autoUpdateTime;"
	}
	this.OrmParam = ormP
	return this
}

// 是否在初次创建时自动填充
func (this *DateTime) SetIsAutoNowAdd(is bool) *DateTime {
	ormP := ""
	this.IsAutoNowAdd = is
	if this.IsAutoNowAdd {
		ormP += "autoCreateTime;"
	}
	if this.IsAutoNow {
		ormP += "autoUpdateTime;"
	}
	this.OrmParam = ormP
	return this
}
func (f *DateTime) CreateConfig() {
	isNull := "null;"
	if !f.Nullable {
		isNull = "not null;"
		f.Type = F_DATETIME
	} else {
		f.Type = "*" + F_DATETIME

	}
	f.Orm = fmt.Sprintf(`gorm:"%s%s"`, isNull, f.OrmParam)
}

type BaseRelation struct {
	BaseType
	OrmParam string
	//SrcDir   string
	Table Table
	Fk    string
	Ref   string
}

type ManyToOne struct {
	BaseRelation
}

func (this *ManyToOne) SetName(v string) *ManyToOne         { this.Name = v; return this }
func (this *ManyToOne) SetType(v string) *ManyToOne         { this.Type = v; return this }
func (this *ManyToOne) SetEntityType(v string) *ManyToOne   { this.EntityType = v; return this }
func (this *ManyToOne) SetCnName(v string) *ManyToOne       { this.CnName = v; return this }
func (this *ManyToOne) SetNullable(v bool) *ManyToOne       { this.Nullable = v; return this }
func (this *ManyToOne) SetDefaultValue(v string) *ManyToOne { this.DefaultValue = v; return this }
func (this *ManyToOne) SetOrm(v string) *ManyToOne          { this.Orm = v; return this }
func (this *ManyToOne) SetSrc(v string) *ManyToOne          { this.Src = v; return this }
func (this *ManyToOne) SetSrcDir(v string) *ManyToOne       { this.SrcDir = v; return this }
func (this *ManyToOne) SetFk(v string) *ManyToOne           { this.Fk = v; return this }
func (this *ManyToOne) SetRef(v string) *ManyToOne          { this.Ref = v; return this }

func (f *ManyToOne) CreateConfig() {
	srcDir := ""
	if f.SrcDir != "" {
		srcDir = f.SrcDir + "."
	}
	f.Type = F_M2O + srcDir + f.Src
	f.Ref = "id"
	isNull := ""
	f.EntityType = "M2O"
	if !f.Nullable {
		isNull = "not null;"
	}
	f.OrmParam = ""
	f.Orm = fmt.Sprintf(`gorm:"%s%s"`, isNull, f.OrmParam)
}

type OneToMany struct {
	BaseRelation
}

func (this *OneToMany) SetName(v string) *OneToMany         { this.Name = v; return this }
func (this *OneToMany) SetType(v string) *OneToMany         { this.Type = v; return this }
func (this *OneToMany) SetEntityType(v string) *OneToMany   { this.EntityType = v; return this }
func (this *OneToMany) SetCnName(v string) *OneToMany       { this.CnName = v; return this }
func (this *OneToMany) SetNullable(v bool) *OneToMany       { this.Nullable = v; return this }
func (this *OneToMany) SetDefaultValue(v string) *OneToMany { this.DefaultValue = v; return this }
func (this *OneToMany) SetOrm(v string) *OneToMany          { this.Orm = v; return this }
func (this *OneToMany) SetSrc(v string) *OneToMany          { this.Src = v; return this }
func (this *OneToMany) SetSrcDir(v string) *OneToMany       { this.SrcDir = v; return this }
func (this *OneToMany) SetFk(v string) *OneToMany           { this.Fk = v; return this }

//这里O2M好像不需要设置ref
//func (this *OneToMany) SetRef(v string) *OneToMany          { this.Ref = v; return this }

func (f *OneToMany) CreateConfig() {
	srcDir := ""
	if f.SrcDir != "" {
		srcDir = f.SrcDir + "."
	}
	f.Type = F_O2M + srcDir + f.Src
	f.EntityType = "O2M"
	if f.Fk == "" {
		f.Fk = fmt.Sprintf("%sId", f.Table.Name)
	}
	isNull := ""
	if !f.Nullable {
		isNull = "not null;"
	}
	f.OrmParam = fmt.Sprintf("foreignKey:%s;", ez.SnakeString(f.Fk))
	//f.OrmParam = fmt.Sprintf("foreignKey:%s;", f.Fk)
	f.Orm = fmt.Sprintf(`gorm:"%s%s"`, isNull, f.OrmParam)
}

type OneToOne struct {
	BaseRelation
	IsControl bool
}

func (this *OneToOne) SetName(v string) *OneToOne         { this.Name = v; return this }
func (this *OneToOne) SetType(v string) *OneToOne         { this.Type = v; return this }
func (this *OneToOne) SetEntityType(v string) *OneToOne   { this.EntityType = v; return this }
func (this *OneToOne) SetCnName(v string) *OneToOne       { this.CnName = v; return this }
func (this *OneToOne) SetNullable(v bool) *OneToOne       { this.Nullable = v; return this }
func (this *OneToOne) SetDefaultValue(v string) *OneToOne { this.DefaultValue = v; return this }
func (this *OneToOne) SetOrm(v string) *OneToOne          { this.Orm = v; return this }
func (this *OneToOne) SetSrc(v string) *OneToOne          { this.Src = v; return this }
func (this *OneToOne) SetSrcDir(v string) *OneToOne       { this.SrcDir = v; return this }
func (this *OneToOne) SetFk(v string) *OneToOne           { this.Fk = v; return this }

func (this *OneToOne) SetRef(v string) *OneToOne { this.Ref = v; return this }

func (f *OneToOne) SetIsControl(ctrl bool) *OneToOne {
	f.IsControl = ctrl
	return f
}
func (f *OneToOne) CreateConfig() {
	srcDir := ""
	if f.SrcDir != "" {
		srcDir = f.SrcDir + "."
	}
	f.Type = F_O2O + srcDir + f.Src
	f.EntityType = "O2O"
	isNull := ""
	if f.Nullable {
		isNull = "null;"
	}
	if f.IsControl {
		if f.Ref == "" {
			f.Ref = "id"
		}
		//has one
		//f.OrmParam = fmt.Sprintf("rel(one);foreignKey:%s", f.Fk)
		f.OrmParam = fmt.Sprintf("foreignKey:%s;references:%s;", ez.CamelString(f.Fk), ez.CamelString(f.Ref))
	} else {
		//belongto
		//1对1的反向没实现
		f.OrmParam = fmt.Sprintf("foreignKey:%s;", ez.CamelString(f.Fk))
	}
	f.Orm = fmt.Sprintf(`gorm:"%s%s"`, isNull, f.OrmParam)
}

type ManyToMany struct {
	BaseRelation
	IsControl bool
	LinkTable string
}

func (this *ManyToMany) SetName(v string) *ManyToMany         { this.Name = v; return this }
func (this *ManyToMany) SetType(v string) *ManyToMany         { this.Type = v; return this }
func (this *ManyToMany) SetEntityType(v string) *ManyToMany   { this.EntityType = v; return this }
func (this *ManyToMany) SetCnName(v string) *ManyToMany       { this.CnName = v; return this }
func (this *ManyToMany) SetNullable(v bool) *ManyToMany       { this.Nullable = v; return this }
func (this *ManyToMany) SetDefaultValue(v string) *ManyToMany { this.DefaultValue = v; return this }
func (this *ManyToMany) SetOrm(v string) *ManyToMany          { this.Orm = v; return this }
func (this *ManyToMany) SetSrc(v string) *ManyToMany          { this.Src = v; return this }
func (this *ManyToMany) SetSrcDir(v string) *ManyToMany       { this.SrcDir = v; return this }
func (this *ManyToMany) SetFk(v string) *ManyToMany           { this.Fk = v; return this }
func (this *ManyToMany) SetRef(v string) *ManyToMany          { this.Ref = v; return this }
func (this *ManyToMany) SetLinkTable(v string) *ManyToMany    { this.LinkTable = v; return this }

func (f *ManyToMany) SetIsControl(ctrl bool) *ManyToMany {
	f.IsControl = ctrl
	return f
}
func (f *ManyToMany) CreateConfig() {
	srcDir := ""
	if f.SrcDir != "" {
		srcDir = f.SrcDir + "."
	} else {
		f.SrcDir = f.Table.Path
	}
	f.Type = F_M2M + srcDir + f.Src
	f.EntityType = "M2M"
	isNull := ""
	if !f.Nullable {
		isNull = "not null;"
	}
	if f.IsControl {
		//f.Fk = fmt.Sprintf("%sId", ez.SnakeString(f.Table.Path+f.Table.Name))
		//f.Ref = fmt.Sprintf("%sId", ez.SnakeString(f.SrcDir+f.Src))
		f.Fk = fmt.Sprintf("%sId", ez.SnakeString(f.Table.Name))
		f.Ref = fmt.Sprintf("%sId", ez.SnakeString(f.Src))
		f.LinkTable = fmt.Sprintf("%s_m2m_%s", ez.SnakeString(f.Table.Path+f.Table.Name), ez.SnakeString(f.SrcDir+f.Src))
		f.OrmParam = fmt.Sprintf("many2many:%s_m2m_%s;", ez.SnakeString(f.Table.Path+f.Table.Name), ez.SnakeString(f.SrcDir+f.Src))
	} else {
		//f.Ref = fmt.Sprintf("%s_id", ez.SnakeString(f.Table.Path+f.Table.Name))
		//f.Fk = fmt.Sprintf("%s_id", ez.SnakeString(f.SrcDir+f.Src))
		f.Ref = fmt.Sprintf("%s_id", ez.SnakeString(f.Table.Name))
		f.Fk = fmt.Sprintf("%s_id", ez.SnakeString(f.Src))
		f.LinkTable = fmt.Sprintf("%s_m2m_%s", ez.SnakeString(f.SrcDir+f.Src), ez.SnakeString(f.Table.Path+f.Table.Name))
		f.OrmParam = fmt.Sprintf("many2many:%s_m2m_%s;", ez.SnakeString(f.SrcDir+f.Src), ez.SnakeString(f.Table.Path+f.Table.Name))
	}
	f.Orm = fmt.Sprintf(`gorm:"%s%s"`, isNull, f.OrmParam)
}

type File struct {
	BaseType
}

func (this *File) SetName(v string) *File         { this.Name = v; return this }
func (this *File) SetType(v string) *File         { this.Type = v; return this }
func (this *File) SetEntityType(v string) *File   { this.EntityType = v; return this }
func (this *File) SetCnName(v string) *File       { this.CnName = v; return this }
func (this *File) SetNullable(v bool) *File       { this.Nullable = v; return this }
func (this *File) SetDefaultValue(v string) *File { this.DefaultValue = v; return this }
func (this *File) SetOrm(v string) *File          { this.Orm = v; return this }
func (this *File) SetSrc(v string) *File          { this.Src = v; return this }

func (json *File) CreateConfig() {
	json.Type = F_FILE
	json.EntityType = "File"
	isNull := ""
	if !json.Nullable {
		isNull = "not null;"
	}
	json.Orm = fmt.Sprintf(`gorm:"%stype:text;" edit_type:"file"`, isNull)
}

type Files struct {
	BaseType
}

func (this *Files) SetName(v string) *Files         { this.Name = v; return this }
func (this *Files) SetType(v string) *Files         { this.Type = v; return this }
func (this *Files) SetEntityType(v string) *Files   { this.EntityType = v; return this }
func (this *Files) SetCnName(v string) *Files       { this.CnName = v; return this }
func (this *Files) SetNullable(v bool) *Files       { this.Nullable = v; return this }
func (this *Files) SetDefaultValue(v string) *Files { this.DefaultValue = v; return this }
func (this *Files) SetOrm(v string) *Files          { this.Orm = v; return this }
func (this *Files) SetSrc(v string) *Files          { this.Src = v; return this }

func (json *Files) CreateConfig() {
	json.Type = F_FILES
	json.EntityType = "Files"
	isNull := ""
	if !json.Nullable {
		isNull = "not null;"
	}
	json.Orm = fmt.Sprintf(`gorm:"%stype(text);" edit_type:"files"`, isNull)
}

//
//1	文本_TEXT	string
//2	整数Integer	integer
//3	布尔Boolean|Switch	boolean
//4	浮动float	float
//5	日期DateTime	datetime
//6	数组Array	array
//7	多对一ManyToOne	21
//8	一对多OneToMany	12
//9	多对多ManyToMany	22
//10	一对一OneToOne	11
//11	长文本_Text	text
//12	JSON(占位)	json
